package com.ganzi.ganziitem.netty;

import com.ganzi.ganziitem.Service.CollectionServer;
import com.ganzi.ganziitem.core.Information;
import com.ganzi.ganziitem.core.State;
import com.ganzi.ganziitem.netty.util.CRC16M;
import com.ganzi.ganziitem.netty.util.FormatTransfer;
import com.ganzi.ganziitem.netty.util.NettyChannelMap;
import com.ganzi.ganziitem.rabbit.BaseUtil;
import com.ganzi.ganziitem.rabbit.SendUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
@ChannelHandler.Sharable
public class NettyServerHandler extends SimpleChannelInboundHandler {

    private static Logger log = LoggerFactory.getLogger(NettyServerHandler.class);

    @Autowired
    SendUtil senUtil;
    @Value("${rabbit.shoolQueue}")
    private String shoolQueue;

    //收到数据
    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, Object o) throws Exception  {
        ByteBuf buf = (ByteBuf) o;
        byte[] data = new byte[buf.readableBytes()];
        buf.readBytes(data);
        int head = data[0]&0xff;
        if(head != BaseUtil.HEART_BEAT_HEAD){//非心跳
            int length = FormatTransfer.hBytesToShort(FormatTransfer.copy(data, BaseUtil.MESSAGE_LENGTH_FIRST_BIT, BaseUtil.MESSAGE_LENGTH_ALL_BIT));// 消息长度
            if(length <=0 )return;
            int type = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, BaseUtil.MESSAGE_TYPE_FIRST_BIT, BaseUtil.MESSAGE_TYPE_ALL_BIT));
            if(type == BaseUtil.QUERY_MESSAGE){
                log.info("收到状态消息："+CRC16M.getBufHexStr(data));
                if((length-BaseUtil.MESSAGE_LENGTH_ADJUSTMENT)%(BaseUtil.FAN_LENGTH*2) != 0){//有效数据不是盘管数据的整数倍  脏数据
                    log.info("消息不完整");
                    return;
                }
                String ip = NettyChannelMap.getIp(channelHandlerContext.channel());
                if(StringUtils.isNotBlank(ip)){
                    Information info = Information.builder().ip(ip).channelName(shoolQueue).type(0).build();//type 0 表示查询消息
                    List<State> listState =  new ArrayList<State>();
                    int allFan = (length-BaseUtil.MESSAGE_LENGTH_ADJUSTMENT)/ (BaseUtil.FAN_LENGTH*2);
                    int firstAddr = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, BaseUtil.COLLECTION_FIRST_ADDR_FIRST_BIT, BaseUtil.COLLECTION_FIRST_ADDR_LENGTH));
                    if(firstAddr < BaseUtil.FIRST_ADDR || firstAddr%BaseUtil.FIRST_ADDR != 0){
                        log.info("首地址异常");
                        return;
                    }
                    //根据首地址计算出起始盘管编号
                    int baseFanNUmber = firstAddr == BaseUtil.FIRST_ADDR ? 1:((firstAddr-BaseUtil.FIRST_ADDR)/BaseUtil.FAN_LENGTH)+1;
                    for(int a=0;a<allFan;a++){
                        //开关机
                        int scrpos_one = getCollectorAddr(a,1);//第1个寄存器
                        //模式
                        int scrpos_tow = getCollectorAddr(a,2);//第2个寄存器
                        //风速
                        int scrpos_three = getCollectorAddr(a,3);//第3个寄存器
                        //在线
                        int scrpos_four = getCollectorAddr(a,4);//第4个寄存器
                        //循环
                        int scrpos_five = getCollectorAddr(a,5);//第5个寄存器
                        //温度
                        int scrpos_six = getCollectorAddr(a,6);//第6个寄存器

                        //12和13表示记录的空调使用时间  共4个字节；
                        int scrpos_twelve = getCollectorAddr(a,12);//第12个寄存器
                        int scrpos_thirteen = getCollectorAddr(a,13);//第13个寄存器
                        //14和15表示记录的新风使用时间  共4个字节；
                        int scrpos_fourteen = getCollectorAddr(a,14);//第14个寄存器
                        int scrpos_fifteen = getCollectorAddr(a,15);//第15个寄存器

                        int switchgear = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_one, 2));//开关机
                        int model = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_tow, 2));//模式
                        int windSpeed = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_three, 2));//风速
                        int circulate = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_five, 2));//循环
                        int temperature = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_six, 2));//温度
                        int onLine = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_four, 2))==1 ? 1:2;//在线状态

                        long kt_time = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_twelve, 2), FormatTransfer.copy(data, scrpos_thirteen, 2));
                        long new_time = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, scrpos_fourteen, 2), FormatTransfer.copy(data, scrpos_fifteen, 2));
                        State state = State.builder().fanNumber(String.valueOf(baseFanNUmber+a)).ktTime(kt_time).newTime(new_time)
                                .isOnLine(onLine)
                                .circulate(circulate)
                                .model(model)
                                .temperature(temperature)
                                .windSpeed(windSpeed)
                                .switchgear(switchgear)
                                .build();
                        listState.add(state);
                    }
                    info.setState(listState);
                    senUtil.sendMessageToServer(info);
                }
            }else if(type ==  BaseUtil.CONTROL_MESSAGE){
                log.info("收到控制成功的消息："+CRC16M.getBufHexStr(data));
                String ip = NettyChannelMap.getIp(channelHandlerContext.channel());
                if(StringUtils.isNotBlank(ip)){
                    int fanNumber = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, BaseUtil.COLLECTION_FIRST_ADDR_FIRST_BIT, BaseUtil.COLLECTION_FIRST_ADDR_LENGTH));
                    int controlType = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, BaseUtil.TYPE_FIRST_BIT, BaseUtil.TYPE_LENGTH));
                    int value = FormatTransfer.byteToIntForHex(FormatTransfer.copy(data, 10, 2));
                    State state = State.builder().fanNumber(String.valueOf(fanNumber)).build();
                    if(controlType == BaseUtil.TURN){
                        state.setSwitchgear(value);
                    }else if(controlType == BaseUtil.MODEL){
                        state.setModel(value);
                    }else if(controlType == BaseUtil.WIND){
                        state.setWindSpeed(value);
                    }else if(controlType == BaseUtil.TEMP){
                        state.setTemperature(value);
                    }else if(controlType == BaseUtil.LOOP){
                        state.setCirculate(value);
                    }
                    Information info = Information.builder().ip(ip).channelName(shoolQueue).type(1).build();//type 1 表示控制消息
                    List<State> listState =  new ArrayList<State>();
                    listState.add(state);
                    info.setState(listState);
                    senUtil.sendMessageToServer(info);
                }
            }else{
                log.info("无效消息："+CRC16M.getBufHexStr(data));
            }
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("client handlerAdded:" + ctx.channel().remoteAddress());
        String ip = NettyChannelMap.getIp(ctx.channel());
        if(StringUtils.isNotBlank(ip)){
            synchronized (NettyServerListener.channelMap){
                NettyServerListener.channelMap.put(ip,ctx);
            }
            senUtil.sendStateToServer(ip,1,1);//采集器在线
        }else{
            log.info("client ip null");
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("client channelActive:" + ctx.channel().remoteAddress());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("client channelInactive" + ctx.channel().remoteAddress());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        String ip = NettyChannelMap.getIp(ctx.channel());
        if(StringUtils.isNotBlank(ip)){
            boolean hasKey = NettyServerListener.channelMap.containsKey(ip);
            if(hasKey){
                synchronized (NettyServerListener.channelMap){
                    NettyServerListener.channelMap.remove(ip);
                    log.info("remove client ip:"+ip);
                }
                senUtil.sendStateToServer(ip,1,2);//采集器离线
            }
        }else{
            log.info("client ip null");
        }
        log.info("client handlerRemoved");
    }
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt)throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;
            if (e.state() == IdleState.READER_IDLE) {
                String ip = NettyChannelMap.getIp(ctx.channel());
                log.error("读超时|" + ip + "|idle timeout");
                throw new Exception("idle exception");
            } else if (e.state() == IdleState.WRITER_IDLE) {
                log.error("写超时");
            } else if (e.state() == IdleState.ALL_IDLE) {
                log.info("--- All_IDLE ---|");
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)throws Exception {
        cause.printStackTrace();
        log.error("client exceptionCaught"+cause.getMessage());
        ReferenceCountUtil.release(cause);
        ctx.close();
    }

    /**
     * 获取第a个盘管的第b寄存器的首地址
     * */
    public int getCollectorAddr(int a,int b){
        int addr =0;
        if(a>=0 && b>0){
            int base = BaseUtil.FAN_VALID_DATA_FIRST_BIT;
            int size = BaseUtil.FAN_LENGTH_BIT;
            addr = base+(a*size)+((b-1)*2);
        }
        return addr;
    }
}
